/****************************************************************************

	Printing Gantt Chart
	Course	: CSC3150
	Author	: Tsang Hing Chung
	Date	: 29 September, 2009
	Version	: 1.0

	Acknowledgement: The code is originally created by Dr. Wong Tsz Yeung

****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <math.h>

#include "GanttChart.h"

#ifndef STRING_BUFFER_MAX
#define STRING_BUFFER_MAX 1024
#endif

void printGanttChart(struct jobNode *head, time_t cur_time)
{
	if (!head){
		fprintf(stderr, "Error(%s:%d): head == NULL\n", __FILE__, __LINE__);
		return;
	}

	struct jobNode *ptr;
	int id = 0;
	float epsilon = 0.05;
	const char MIXED_JOB = 255;
	int firstDecimalPlace = 0;

	const int color_range = 6;
	char *color[] = { 
		"\033[1;36m", 
		"\033[1;31m", 
		"\033[1;32m", 
		"\033[1;33m", 
		"\033[1;34m", 
		"\033[1;35m", 
	};
	char *color_end = "\033[0m";
	char hline[STRING_BUFFER_MAX];
	char buf[STRING_BUFFER_MAX];
	int i, j, len, color_i = 0;

#ifdef DEBUG
	// print the timeline array one by one
	for (id = 1, ptr=head; ptr; ptr=ptr->next, id++){
		printf("Job %d's timeline\n", id);
		printf("timelineIndex = %d\n", ptr->timelineIndex);
		for (i = 0; i < ptr->timelineIndex; i++){
			printf("[%d][0] = %5.2f ", i, ptr->timeline[i][0]);
			if (i != ptr->timelineIndex - 1){
				printf(" | ");
			}
		}
		printf("\n");
		for (i = 0; i < ptr->timelineIndex; i++){
			printf("[%d][1] = %5.2f ", i, ptr->timeline[i][1]);
			if (i != ptr->timelineIndex - 1){
				printf(" | ");
			}
		}
		printf("\n\n");
	}
	
#endif

	fprintf(stdout, "%sGantt Chart%s\n", color[1], color_end);

	i = 0;
	len = i + 9;
	for( ; i < len; i++)
		hline[i] = '-';
	hline[i++] = '+';
	len = i + (cur_time + 1) * 2 + 1;
	for( ; i < len; i++)
		hline[i] = '-';
	hline[i] = '\0';

	fprintf(stdout, "  Time   | ");
	for(i = 0; i <= cur_time/10; i++)
		fprintf(stdout, " %d                  ", i);
	fprintf(stdout, "\n");

	fprintf(stdout, "         | ");
	for(i = 0; i <= cur_time; i++)
		fprintf(stdout, " %d", i%10);
	fprintf(stdout, " \n");

	memset(buf, 0, sizeof(buf));
	id = 0;
	for (ptr = head; ptr; ptr = ptr->next){
		id++;
		color_i = id % color_range;
		fprintf(stdout, "%s\n", hline);
		fprintf(stdout, "  %sJob %-2d%s | ", color[color_i], id, color_end);

		// check if the actual start time is >= the expected,
		// allowing an error less than epsilon
		if ((ptr->timeline[0][0] + epsilon) > ptr->arrivalTime){
			for(i = 0; i < ptr->arrivalTime; i++)
				fprintf(stdout, "  ");
		}
		else{
			// reaching here means the actual start time is earlier than expected
			for(i = 0; i < floor(ptr->timeline[0][0]); i++)
				fprintf(stdout, "  ");
		}

		float cumulativeFloatingPoint = 0;
		for(j = 0; j < ptr->timelineIndex; j++){
			// do some error checking
			if (ptr->timeline[j][0] > ptr->timeline[j][1]){
				fprintf(stderr, "\nImpossible! startTime(%.4f) > endTime(%.4f)\n",
						ptr->timeline[j][0], ptr->timeline[j][1]);
				break;
			}

			float roundedStartTime; // correct to the first decimal place
			float roundedEndTime; // correct to the first decimal place
			int floorStartTime;
			int floorEndTime;
			
			floorStartTime = (int)floor(ptr->timeline[j][0]);
			floorEndTime = (int)floor(ptr->timeline[j][1]);
			roundedStartTime = (ROUND(ptr->timeline[j][0] * 10.0)) / 10.0;
			roundedEndTime = (ROUND(ptr->timeline[j][1] * 10.0)) / 10.0;
			
			// if we have any un-printed cumulative floating point
			if (cumulativeFloatingPoint > 0){
				// print the cumulative if ...
				if ((j > 0) && (floorStartTime > (int)floor(ptr->timeline[j-1][1]))){
					// print out the cumulative floating point
					firstDecimalPlace = ROUND( 10.0 * cumulativeFloatingPoint);
					if (firstDecimalPlace > 10){
						// Not possible! Use a '@' to indicate
						fprintf(stdout, " %s@", color[color_i]);
					}
					else if (firstDecimalPlace == 10){
						fprintf(stdout, " %s#", color[color_i]);
					}
					else if (firstDecimalPlace > 0){
						fprintf(stdout, " %s%d", color[color_i], firstDecimalPlace);
					}
					else{
						//firstDecimalPlace == 0??
						fprintf(stdout, " %s-", color[color_i]);
					}
					if (buf[i]){
						// Means mixed job
						buf[i] = MIXED_JOB;
					}
					else{
						buf[i] = id;
					}
					cumulativeFloatingPoint = 0;
					i++;
				}
			}

			while (i < floorStartTime){
				fprintf(stdout, " %s.", color[color_i]);
				i++;
			}

			// Some job may run for very short period of time, we now handle this case
			if ((ptr->timelineIndex == 1) && ((ptr->timeline[0][1] - ptr->timeline[0][0]) < epsilon) ) {
				fprintf(stdout, " %s-", color[color_i]);
				if (buf[i]){
					// Means mixed job
					buf[i] = MIXED_JOB;
				}
				else{
					buf[i] = id;
				}
				break;
			}
			
			// process the floating point of startTime, if any
			if ((roundedStartTime - i) > epsilon){ // if roundedStartTime > i
				// check if the end time happen on next second
				// or if the end time happen on the same second but the job has finished
				if ( (floorStartTime < floorEndTime) || 
						((floorStartTime == floorEndTime) && (j == (ptr->timelineIndex - 1))) ){
					// add up the cumulative floating point number
					if (floorStartTime < floorEndTime){
						cumulativeFloatingPoint += ( ceil(ptr->timeline[j][0]) - ptr->timeline[j][0] );
					}
					else{
						cumulativeFloatingPoint += ( ptr->timeline[j][1] - ptr->timeline[j][0] );
					}
					// print out the cumulative floating point
					firstDecimalPlace = ROUND( 10.0 * cumulativeFloatingPoint);
					if (firstDecimalPlace > 10){
						// Not possible! Use a '@' to indicate
						fprintf(stdout, " %s@", color[color_i]);
					}
					else if (firstDecimalPlace == 10){
						fprintf(stdout, " %s#", color[color_i]);
					}
					else if (firstDecimalPlace > 0){
						fprintf(stdout, " %s%d", color[color_i], firstDecimalPlace);
					}
					else{
						//firstDecimalPlace == 0??
						fprintf(stdout, " %s-", color[color_i]);
					}
					if (buf[i]){
						// Means mixed job
						buf[i] = MIXED_JOB;
					}
					else{
						buf[i] = id;
					}
					cumulativeFloatingPoint = 0;
					i++;
				}
				// if the integral parts of both start time and stop time are the same
				// add the difference to the cumulative floating point and continue
				else if (floorStartTime == floorEndTime){
					cumulativeFloatingPoint += ( ptr->timeline[j][1] - ptr->timeline[j][0] );
					continue;
				}
			}

			// print out the rest of the integral part of the end time
			while (i < floorEndTime){
				fprintf(stdout, " %s#", color[color_i]);
				buf[i] = id;
				i++;
			}

			// process the floating point of startTime, if any
			if ((roundedEndTime - i) > epsilon){ // if roundedEndTime > i
				if ( (j == (ptr->timelineIndex - 1)) ||
					( (int)floor(ptr->timeline[j+1][0]) > floorEndTime) ){

					firstDecimalPlace = ROUND((ptr->timeline[j][1] - floor(ptr->timeline[j][1])) * 10.0);
					if (firstDecimalPlace > 10){
						// Not possible! Use a '@' to indicate
						fprintf(stdout, " %s@", color[color_i]);
					}
					else if (firstDecimalPlace == 10){
						fprintf(stdout, " %s#", color[color_i]);
					}
					else if (firstDecimalPlace > 0){
						fprintf(stdout, " %s%d", color[color_i], firstDecimalPlace);
					}
					else{
						//firstDecimalPlace == 0??
						fprintf(stdout, " %s-", color[color_i]);
					}
					if (buf[i]){
						// Means mixed job
						buf[i] = MIXED_JOB;
					}
					else{
						buf[i] = id;
					}
					i++;
				}
				// record the floating point part of running time and continue
				else{
					if (ptr->timeline[j+1][0] < ptr->timeline[j][1]){
						fprintf(stderr, 
							"Impossible! The next start time(%.4f) < current stop time(%.4f)\n",
							ptr->timeline[j+1][0], ptr->timeline[j][1]);
						break;
					}
					// else, the next start time happen on the same second of the current stop time
					else{
						// record the floating point part and leave it to the next iteration
						cumulativeFloatingPoint = ptr->timeline[j][1] - floor(ptr->timeline[j][1]);
					}
				}
			}
			// end of processing the floating point of stop time

		}
		fprintf(stdout, "%s\n", color_end);
	}

	fprintf(stdout, "%s\n", hline);
	fprintf(stdout, "  Mixed  | ");
	for(i = 0; i < cur_time; i++){
		if (buf[i] == 0){
			fprintf(stdout, " %sx", "\033[37;1m");
		}
		else if (buf[i] == MIXED_JOB){
			fprintf(stdout, " %sm", "\033[37;1m");
		}
		else{
			fprintf(stdout, " %s%d", color[buf[i] % color_range], buf[i]);
		}
	}
	fprintf(stdout, "%s\n", color_end);
}
